/*!
 Temelia - Hash set performance tests.

 Copyright (C) 2008 Ceata (http://cod.ceata.org/proiecte/temelia).

 @author Dascalu Laurentiu

 This program is free software; you can redistribute it and
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "include/performance.hpp"
#include "include/testutil.hpp"

#include <algorithm>
#include <vector>

extern "C"
{
#include <temelia/hash_set.h>
}

#define OPTIMIZE

#ifdef OPTIMIZE
static int get_digits_optimized(int key)
{
	// The maximum unsigned int is 2147483648
	/*
	 * 10 x if are faster then one division :-P
	 */
	if (key < 10)
		return 1;
	else if (key < 100)
		return 2;
	else if (key < 1000)
		return 3;
	else if (key < 10000)
		return 4;
	else if (key < 100000)
		return 5;
	else if (key < 1000000)
		return 6;
	else if (key < 10000000)
		return 7;
	else if (key < 100000000)
		return 8;
	else if (key < 1000000000)
		return 9;
	else
		return 10;
}
#endif

static int get_digits_number(int key)
{
#ifdef OPTIMIZE
	if (key < 0)
		return get_digits_optimized(-key) - 1;
	return get_digits_optimized(key);
#endif

	int digits = 0;

	if (!key)
		return 0;

	while (key)
	{
		key /= 10;
		digits++;
	}

	return digits - 1;
}

#define MAX_DIGITS    (10)

static int hash_function(void *key, void *context)
{
	return get_digits_number(*(int *) key);
}

static void temelia_hash_set_contains(int size)
{
	hash_set_t hash_set;

	hash_set = hash_set_new(MAX_DIGITS);

	for (int i = 0; i < size; i++)
		hash_set_put(hash_set, default_collection[i], hash_function, NULL,
				int_compare, NULL);

	for (int i = 0; i < size; i++)
		hash_set_contains(hash_set, default_collection[i], hash_function, NULL,
				int_compare, NULL);

	hash_set_delete(hash_set);
}

static void temelia_hash_set_put(int size)
{
	hash_set_t hash_set;

	hash_set = hash_set_new(MAX_DIGITS);

	for (int i = 0; i < size; i++)
		hash_set_put(hash_set, default_collection[i], hash_function, NULL,
				int_compare, NULL);
	hash_set_delete(hash_set);
}

static void temelia_hash_set_remove(int size)
{
	hash_set_t hash_set;

	hash_set = hash_set_new(MAX_DIGITS);

	for (int i = 0; i < size; i++)
		hash_set_put(hash_set, default_collection[i], hash_function, NULL,
				int_compare, NULL);

	for (int i = 0; i < size; i++)
		hash_set_remove(hash_set, default_collection[i], hash_function, NULL,
				int_compare, NULL);

	hash_set_delete(hash_set);
}

void TemeliaPerformance::pt_hash_set(void)
{
	TestUnit testUnit("hash_set");

#ifndef CLUSTER_RUNNING
	ntests = 10;
#endif

	GENERATE_TEMELIA_TEST(testUnit, "contains", hash_set_contains);
	GENERATE_TEMELIA_TEST(testUnit, "put", hash_set_put);
	GENERATE_TEMELIA_TEST(testUnit, "remove", hash_set_remove);

	testUnit.write(fout);
}
